package web.controller.sys;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import framework.captcha.CaptchaServlet;
import framework.crypto.GeneralCrypto;
import framework.security.AuthService;
import framework.security.password.PasswordService;
import framework.security.token.AuthTokenBuilder;
import framework.storage.FileStorage;
import framework.utils.JsonUtil;
import framework.utils.RSAUtil;
import framework.utils.VersionUtil;
import framework.vo.R;
import framework.vo.ResultInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import system.config.DateTimeConfig;
import system.service.sys.SysConfigService;
import system.vo.ConfigUI;
import system.vo.EnumUI;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 匿名访问控制器
 * 可以放置一些允许匿名访问的控制器
 */
@Api(tags = "公众接口")
@RestController
@RequestMapping("/pub")
public class SysPubController {
    private static Instant UP_TIME;
    private static final int SALT_LENGTH = 19;
    @Autowired
    private PasswordService passwordService;
    @Autowired
    private SysConfigService configService;
    @Autowired
    private AuthService authService;
    @Autowired
    private CaptchaServlet captchaServlet;
    @Autowired
    private AuthTokenBuilder tokenBuilder;
    @Autowired
    private GeneralCrypto generalCrypto;
    @Autowired
    private FileStorage fileStorage;
    @Autowired
    private DateTimeConfig dateTimeConfig;

    public SysPubController() {
        UP_TIME = new Date().toInstant();
    }

//    @GetMapping("/404")
//    public void e404() throws Error404Exception {
//        ErrorUtil.notFound();
//    }
//
//    @GetMapping("/403")
//    public void e403() throws Error403Exception {
//        ErrorUtil.forbidden();
//    }
//
//    @GetMapping("/401")
//    public void e401() throws Error401Exception {
//        ErrorUtil.noAuthorization();
//    }
//
//    @GetMapping("/400")
//    public void e400() throws Error400Exception {
//        ErrorUtil.badRequest();
//    }
//
//    @GetMapping("/500")
//    public void e500() throws WebException {
//        ErrorUtil.serverError();
//    }
//
//    @GetMapping("/error")
//    public void error() throws BusinessException {
//        ErrorUtil.error("business error");
//    }


    @ApiOperation("上传文件")
    @PostMapping("/upload")
    public String uploadFile(@RequestPart MultipartFile file) throws ServletException, IOException {
        // 该接口为测试接口，固定文件地址
        String id = "/test/test.bin";
        if (file.getSize() > 50 * 1024 * 1024) {
            throw new ServletException("File size exceeds the maximum allowed size (100MB).");
        }
        this.fileStorage.save(file.getInputStream(), id);
        return id;
    }

//    @ApiOperation("下载文件")
//    @GetMapping("/download")
//    public void uploadFile(HttpServletResponse response) throws ServletException, IOException {
//        // 该接口为测试接口，因定上传文件地址
//        String id = "/test/test.bin";
//        try (InputStream inputStream = this.fileStorage.getStream(id)) {
//            try (ServletOutputStream outputStream = response.getOutputStream()) {
//                response.setHeader("Content-Disposition", "attachment; filename=\"test.bin\"");
//                IOUtils.copy(inputStream, outputStream);
//            }
//        }
//    }

    @PostMapping("/password")
    public Map password(String password, String salt) {
        return passwordDrive(password, salt);
    }

    @GetMapping("/password")
    public Map password() {
        String salt = RandomStringUtils.randomGraph(SALT_LENGTH);
        String password = RandomStringUtils.randomAlphanumeric(12);
        return passwordDrive(password, salt);
    }

    private Map passwordDrive(String password, String salt) {
        String encodePassword = this.passwordService.encode(password, salt);
        //
        Map<String, Object> map = new HashMap<>();
        map.put("salt", salt);
        map.put("password", password);
        map.put("encodePassword", encodePassword);
        if (this.generalCrypto.enable()) {
            map.put("passwordCipher", "gc");
            map.put("passwordValue", this.generalCrypto.encryptAsBase64(password));
        }
        return map;
    }

    /**
     * UI配置
     *
     * @throws IOException
     */
    @ApiOperation("UI配置")
    @GetMapping("/setting")
    public ResultInfo<ConfigUI> setting() throws IOException {
        ConfigUI ui = this.configService.getUI();
        return new ResultInfo<>(ui);
    }

    /**
     * 枚举配置
     */
    @ApiOperation("枚举配置")
    @GetMapping("/enums")
    public ResultInfo<Object> enums(String[] name) {
        if (name == null || name.length == 0) {
            return R.ok(new EnumUI());
        }
        ObjectMapper objectMapper = JsonUtil.newMapper();
        JsonNode jsonNode = objectMapper.valueToTree(new EnumUI());
        ObjectNode dataNode = objectMapper.createObjectNode();
        for (String n : name) {
            dataNode.putIfAbsent(n, jsonNode.get(n));
        }
        return R.ok(dataNode);
    }

    @GetMapping("/status")
    public Map status() {
        Instant instant = new Date().toInstant();
        DateFormat timeF = dateTimeConfig.newDateFormat();
        //
        Map<String, Object> map = new HashMap<>();
        map.put("now_time", timeF.format(Date.from(instant)));
        map.put("up_time", timeF.format(Date.from(UP_TIME)));
        //
        SimpleDateFormat versionF = new SimpleDateFormat("yyyyMMddHHmmss");
        Date version = VersionUtil.jarVersion(this.getClass());
        map.put("version", versionF.format(version));
        map.put("ver_time", timeF.format(version));
        //
        Long accountId = this.authService.getAccountId();
        map.put("auth_userid", accountId);
        map.put("auth_token", this.authService.getSecret());
        //
        return map;
    }

    /**
     * 验证码
     *
     * @param request
     * @param resp
     * @param id
     * @throws IOException
     */
    @ApiOperation("验证码")
    @GetMapping("/captcha")
    public void captcha(HttpServletRequest request, HttpServletResponse resp, @RequestParam String id) throws IOException {
        this.captchaServlet.output(request, resp, id);
    }

    /**
     * 验证码验证
     *
     * @param code
     * @param id
     * @throws IOException
     */
    @ApiOperation("验证码验证")
    @PostMapping("/captcha/validate")
    public ResultInfo validate(@RequestParam String id, @RequestParam String code) throws IOException {
        boolean checked = this.captchaServlet.check(id, code);
        ResultInfo r = new ResultInfo();
        if (!checked)
            r = r.failed("Failure");
        return r;
    }

    /**
     * 生成一个RSA密钥对
     */
    @ApiOperation("生成一个RSA密钥对")
    @GetMapping("/rsa.pem")
    public void rsa(HttpServletResponse response, @RequestParam(defaultValue = "2048") Integer length) throws IOException {
        if (length == null) length = 2048;
        RSAUtil.RSAItem rsaItem = RSAUtil.generateNew(length);
        Base64.Encoder mimeEncoder = Base64.getMimeEncoder(64, "\r\n".getBytes());
        String s = "-----BEGIN RSA PRIVATE KEY-----\r\n"
                + mimeEncoder.encodeToString(rsaItem.getPrivateKey())
                + "\r\n-----END RSA PRIVATE KEY-----"
                + "\r\n-----BEGIN PUBLIC KEY-----\r\n"
                + mimeEncoder.encodeToString(rsaItem.getPublicKey())
                + "\r\n-----END PUBLIC KEY-----";
        response.setContentType("text/plain");
        response.getWriter().write(s);
    }

    /**
     * 获取一般性加密公钥
     */
    @ApiOperation("获取一般性加密公钥")
    @GetMapping("/publicKey")
    public ResultInfo<String> publicKey(HttpServletResponse response) throws IOException {
        return new ResultInfo<>(this.generalCrypto.getPublicKeyContent());
    }

    /**
     * 获取一般性加密公钥
     */
    @ApiOperation("获取一般性加密公钥")
    @GetMapping("/publicKey.pem")
    public void publicKeyPEM(HttpServletResponse response) throws IOException {
        response.setContentType("text/plain");
        response.getWriter().write(this.generalCrypto.getPublicKeyContent());
    }

    /**
     * 获取一般性加密公钥
     */
    @ApiOperation("获取一般性加密公钥")
    @GetMapping("/publicKey.js")
    public String publicKeyJs(HttpServletResponse response, Integer includeHead) throws IOException {
        response.setContentType("application/javascript");
        String publicKeyContent = this.generalCrypto.getPublicKeyContent();
        publicKeyContent = publicKeyContent.replace("\r\n", "");
        if (includeHead != null && includeHead == 0) {
            publicKeyContent = publicKeyContent.replace("-----BEGIN PUBLIC KEY-----", "");
            publicKeyContent = publicKeyContent.replace("-----END PUBLIC KEY-----", "");
        }
        return "var publicKey=\"" + publicKeyContent + "\";";
    }

}
